package gae.testjig.server;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;

import gae.testjig.client.ClientChannelId;

// This is the class that will live long in memory and poll memcache for semaphores
// that permit it to carry-on to next step. The memcache semaphores will be updated
// client sending "advance the state" messages to the server. This stepper class will
// have to be launched via a task queue so that it is allowed a full ten minutes of run time.
public class ConstraintTestStepper {

	private ClientChannelId ccid;
	private MemcacheSemaphore sema;
	private ChannelLogger logger;
	private String seatId;
	private String ownerName;
	
	public ConstraintTestStepper(ClientChannelId clientChannelId, String seatId, String ownerName) {
		logger = new ChannelLogger(clientChannelId);
		this.ccid = clientChannelId;
		this.seatId = seatId;
		this.ownerName = ownerName;
		this.sema = new MemcacheSemaphore(ownerName+seatId);
		this.sema.setUnderway(true);
	}
	
	public void performSteps() {
		try {
			logger.info(ownerName+": BEGIN");
			ConstraintTest ct = new ConstraintTest();
			
			SYNCH(ownerName);
			logger.info(ownerName+": beginning transaction.");
			String txnId = ct.beginTransaction();
			logger.info(ownerName+": transactionId="+txnId);
			
			SYNCH(ownerName);
			logger.info(ownerName+": test for existence of entity with seatId=" + seatId);
			if ( ct.testExists(seatId) ) {
				logger.info(ownerName+": sorry, that seat has already been taken.");
			} else {
				logger.info(ownerName+": that seat has not been taken yet.");
	
				SYNCH(ownerName);
				logger.info(ownerName+": create the seat entity with seatId=" + seatId);
				String key = ct.createSeatEntity(seatId, ownerName);
				logger.info(ownerName+": created seat. entity key = " + key);
			}
	
			SYNCH(ownerName);
			ct.endTransaction();
			logger.info(ownerName+": transaction committed.");
			logger.info(ownerName+": END");
		}
		catch ( AbortException e ) {
			logger.info(ownerName+": ABORT");
		}
		catch ( CommitException e ) {
			logger.info(ownerName+": commit failed: " + e.getMessage());
			logger.info(ownerName+": ABORT");
		}
		catch ( Exception e ) {
			logger.info(ownerName+": exception thrown: " + e.getMessage());
			logger.info(ownerName+": ABORT");
		}

		sendCommandReadyToClient();
		sema.setUnderway(false);
	}

	// our synchronization will work by polling the memcache  for an item keyed  by
	// the owner name. The client will place it there as a signal to advnce. In fact
	// the payload of the object will be either "ADVANCE" or "ABORT", the latter permitting
	// the whole step sequence to be aborted.
	private void SYNCH(String ownerName) throws AbortException {
		logger.info(ownerName+":   --ready--");
		sendCommandReadyToClient();
		sema.poll();
	}
	
	private void sendCommandReadyToClient() {
		ChannelService channelService = ChannelServiceFactory.getChannelService();
		channelService.sendMessage(new ChannelMessage(ccid.getClientId(), "{\"channelId\": \""+ccid.getChannelId()+"\", \"ready\": \""+ownerName+"\"}"));
	}

}
